package msg;

import static org.junit.Assert.*;

import java.util.*;

import msg.visitors.MessageToJson;

import org.json.JSONObject;
import org.junit.Test;

import user.*;

import common.DBTest;
import common.dal.Transaction;
import common.exceptions.*;
import common.interfaces.IPersistent;

@SuppressWarnings({"static-method", "javadoc"})
public class MessageManagerTest extends DBTest {
	static MessageManager	$;
	
	@Override
	protected IPersistent setPersistent(Transaction t) {
		$ = MessageManager.getNewInstance(t);
		return $;
	}
	
	@Override
	protected IPersistent getPersistent() {
		return $;
	}
	
	@Test(expected = InvalidUserException.class)
	public void testCreateNewMessageBadPoster() throws Exception {
		$.createNewMessage(new Location(0, 0), "", getFakeUser());
	}
	
	@Test(expected = ArithmeticException.class)
	public void testCreateNewEventBadCapacity() throws Exception {
		$.createNewEventMessage(new Location(0, 0), "", getUser(), getTomorrow(), 0);
	}
	
	@Test(expected = ArithmeticException.class)
	public void testCreateNewEventBadTime() throws Exception {
		Calendar cal = Calendar.getInstance();
		Thread.sleep(1);
		$.createNewEventMessage(new Location(0, 0), "", getUser(), cal, 10);
	}
	
	@Test
	public void testCreateNewMessage() throws Exception {
		for (int i = 0; i < 3; i++) {
			Message msg = $.createNewMessage(new Location(0, 0), "", getUser());
			assertTrue(msg.getId() >= i);
		}
	}
	
	@Test
	public void testLoad() throws Exception {
		Message msg = $.createNewMessage(new Location(0, 1), "", getUser());
		MessageToJson jsoner = new MessageToJson();
		JSONObject expected = msg.accept(jsoner);
		JSONObject actual = $.get(msg.getId()).accept(jsoner);
		assertEquals(expected, actual);
	}
	
	@Test(expected = InvalidKeyException.class)
	public void testLoadDoesntExist() throws Exception {
		$.get(0L);
	}
	
	@Test(expected = InvalidUserException.class)
	public void testGetAllUserMessagesDoesntExist() throws Exception {
		$.getAllUserPosts(getFakeUser());
	}
	
	@Test
	public void testGetAllUserMessages() throws Exception {
		User newUser = LoginManager.getNewInstance(t)
				.registerUser(getUser().getLogin() + "_", "new user", PASSWORD);
		try {
			Set<Message> set = new HashSet<>();
			for (int i = 0; i < 3; i++) {
				set.add($.createNewMessage(new Location(0, 0), "msg #" + i, getUser()));
			}
			$.createNewMessage(new Location(1, 1), "bla bla", newUser);
			assertEquals(set, new HashSet<>($.getAllUserPosts(getUser())));
		} finally {
			$.removeAllUserMessagesAndEvents(newUser);
			LoginManager.getNewInstance(t).deleteUser(newUser.getLogin(), PASSWORD);
		}
	}
	
	@Test(expected = InvalidUserException.class)
	public void testGetAllUserEventsDoesntExist() throws Exception {
		$.getallUserEvents(getFakeUser(), null, true);
	}
	
	@Test(expected = InvalidUserException.class)
	public void testGetAllUserMessageOnlyDoesntExist() throws Exception {
		$.getallUserMessagesOnly(getFakeUser(), null);
	}
	
	@Test
	public void testGetAllUserEventsAndMessagesOnly() throws Exception {
		Message msg = $.createNewMessage(new Location(1, 1), "bla bla", getUser());
		EventMessage event = $.createNewEventMessage(new Location(1, 1), "bla bla", getUser(), getTomorrow(), 10);
		Set<EventMessage> events = new HashSet<>(Arrays.asList(event));
		Set<Message> msgs = new HashSet<>(Arrays.asList(msg));
		assertEquals(events, new HashSet<>($.getallUserEvents(getUser(), null, true)));
		assertEquals(msgs, new HashSet<>($.getallUserMessagesOnly(getUser(), null)));
	}
	
	@Test(expected = InvalidUserException.class)
	public void testRemoveAllUserMessagesDoesntExist() throws Exception {
		$.removeAllUserMessagesAndEvents(getFakeUser());
	}
	
	@Test
	public void testRemoveAllUserMessages() throws Exception {
		Message msg = $.createNewMessage(new Location(0, 0), "", getUser());
		$.createNewEventMessage(msg.getLocation(), msg.getContent(), msg.getPoster(), getTomorrow(), 10);
		
		assertEquals(2, $.getAllUserPosts(getUser()).size());
		$.removeAllUserMessagesAndEvents(getUser());
		assertTrue($.getAllUserPosts(getUser()).isEmpty());
	}
	
	@Test
	public void testGetAll() throws Exception {
		Set<Message> expected = new HashSet<>(Arrays.asList($
				.createNewEventMessage(new Location(0, 0), "", getUser(), getTomorrow(), 1), $
				.createNewMessage(new Location(0, 0), "", getUser())));
		assertEquals(expected, new HashSet<>($.getAll()));
	}
	
	@Test
	public void testDifferentIdsSameSession() throws Exception {
		long id1 = $.createNewEventMessage(new Location(0.5, 1), "content", getUser(), getTomorrow(), 10).getId();
		$.delete(id1);
		long id2 = $.createNewEventMessage(new Location(0.5, 1), "content", getUser(), getTomorrow(), 10).getId();
		assertEquals(id2, id1 + 1);
		$.delete(id1);
		long id3 = $.createNewEventMessage(new Location(0.5, 1), "content", getUser(), getTomorrow(), 10).getId();
		assertEquals(id3, id2 + 1);
	}
	
	@Test
	public void testDifferentIdsDifferentSession() throws Exception {
		long id1 = $.createNewEventMessage(new Location(0.5, 1), "content", getUser(), getTomorrow(), 10).getId();
		$.delete(id1);
		t.commit();
		t = new Transaction();
		$ = MessageManager.getNewInstance(t);
		long id2 = $.createNewEventMessage(new Location(0.5, 1), "content", getUser(), getTomorrow(), 10).getId();
		assertEquals(id2, id1 + 1);
		$.delete(id2);
		t.commit();
		t = new Transaction();
		$ = MessageManager.getNewInstance(t);
		long id3 = $.createNewEventMessage(new Location(0.5, 1), "content", getUser(), getTomorrow(), 10).getId();
		assertEquals(id3, id2 + 1);
	}
	
	@Test
	public void testAddressSaved() throws Exception {
		Location loc = new Location(0.5, 2, "add");
		long id = $.createNewMessage(loc, "msg", getUser()).getId();
		assertEquals(loc, $.get(id).getLocation());
		id = $.createNewEventMessage(loc, "msg", getUser(), getTomorrow(), 10).getId();
		assertEquals(loc, $.get(id).getLocation());
	}
	
	@Test
	public void testRadiusTrivial() throws Exception {
		Message msg = $.createNewMessage(new Location(1.1, 0.1), "msg", getUser());
		assertEquals(new HashSet<>(Arrays.asList(msg)), new HashSet<>($.getAllMessagesInRadius(new Location(1.1,
				0.1), 0, null)));
	}
}
